﻿using UnityEngine;
using System.Collections.Generic;
using Action = System.Action;

/// <summary>
/// This script manages the bouncing behavior of the ball.
/// </summary>
public class BallBounce : MonoBehaviour {
	
	/// <summary>
	/// Gets or sets the vector conveying the movement speed and direction of the ball.
	/// </summary>
	/// <value>
	/// The current velocity.
	/// </value>
	public Vector3 Velocity { get; private set; }
	
	/// <summary>
	/// Event fired when the ball reaches a goal area.
	/// </summary>
	public event Action OnGoalReached;
	
	/// <summary>
	/// The position at which the ball begins for each attempt.
	/// </summary>
	Vector3 StartPosition;
	
	// Use this for initialization
	void Start () {
		StartPosition = transform.position;
	}
	
	void OnCollisionEnter(Collision collision)
	{
		// If the ball has contacted a goal box, stop processing, the player has succeeded
		if (collision.gameObject.GetComponent<GoalBox>() != null)
		{
			OnGoalReached();
			return;
		}
		
		// Before performing reflection operations for the bounce effect, the normals being used to determine the reflection
		// must first be compared with those the given blocks were intended to reflect from (to prevent collision with seams)
		ValidNormals validator = collision.gameObject.GetComponent<ValidNormals>();
		if (validator != null)
		{
			foreach (ContactPoint contact in collision.contacts) foreach (Vector3 validNormal in validator.NormalList)
			{
				if (Vector3.Angle(contact.normal.normalized, validNormal) < 45F)
				{
					if (Vector3.Dot(contact.normal.normalized, Velocity.normalized) <= 0)
					{
						Velocity = Vector3.Reflect(Velocity, validNormal);
						return;
					}
				}
			}
		}
	}
	
	void OnCollisionStay(Collision collision)
	{
		// The ball being embedded after player success is of no concern
		if (Velocity == Vector3.zero)
			return;
		
		// Hopefully the code here is unnecessary, but a couple of test cases have been observed where the ball penetrates
		// the wall completely. This code is intended to turn the ball back if for some reason it doesn't bounce.
		// It's a hack, but to stop now and change how the walls are built would take more time than I have to deliver this.
		transform.position -= Velocity.normalized;
		Velocity *= -1F;
	}
	
	/// <summary>
	/// Additively modifies the current velocity of the ball.
	/// </summary>
	/// <param name='velocity'>
	/// Current velocity of the ball.
	/// </param>
	public void AddVelocity(Vector3 velocity)
	{
		Velocity += velocity;
	}
	
	/// <summary>
	/// Halts all of the ball's motion.
	/// </summary>
	public void Stop()
	{
		Velocity = Vector3.zero;
	}
	
	/// <summary>
	/// Resets the ball to its initial state.
	/// </summary>
	public void Reset()
	{
		Velocity = Vector3.zero;
		
		transform.position = StartPosition;
	}
	
	/// <summary>
	/// Apply changes in position due to velocity.
	/// </summary>
	void FixedUpdate()
	{
		transform.position += Velocity * Time.deltaTime;
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}
